//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "cthread.h"
#include "cprocess.h"
#include "cevent.h"
#include "pseudo.h"
#include "pcentral.h"
#include "helper.h"
#include <aura.h>
#include <elacs.h>

#define Lock(hCritical)     do { \
                                AuraCriticalSection_Enter(hCritical); \
                            } while(0)

#define Unlock(hCritical)   do { \
                                AuraCriticalSection_Leave(hCritical); \
                            } while(0)

CThread::CThread(IProcess * pOwner, Address userEntry, Address userArgs)
    : m_threadId(AURA_INVALID_TID),
      m_hThread(NULL),
      m_userEntry(userEntry),
      m_userArgs(userArgs),
      m_pOwner(pOwner),
      m_hObjLock(NULL),
      m_threadState(ThreadState_Unstarted),
      m_quitCode(E_THREAD_ABORTED),
      m_hQuitEvent(NULL),
      m_hTlsLock(NULL)
{
    DLinkNode::Initialize();
    assert(NULL != m_pOwner);
    DLinkNode::Initialize();
    m_name.Copy(L"");
}

CThread::~CThread()
{
    if (NULL != m_hTlsLock) {
        AuraCriticalSection_Destroy(m_hTlsLock);
        m_hTlsLock = NULL;
    }

    if (NULL != m_hObjLock) {
        AuraCriticalSection_Destroy(m_hObjLock);
        m_hObjLock = NULL;
    }

    if (NULL != m_hQuitEvent) {
        AuraEvent_Destroy(m_hQuitEvent);
        m_hQuitEvent = NULL;
    }

    if (NULL != m_hThread) {
        AuraThread_Destroy(m_hThread);
        m_hThread = NULL;
    }
}

ECode CThread::InitializeAsMainThread()
{
    ECode ec = NOERROR;

    ec = Initialize();
    if (FAILED(ec)) {
        return ec;
    }

    m_threadId = AuraThread_GetCurrentId();
    ec = (ECode)AuraThread_Open((Aura_ThreadId)m_threadId, &m_hThread);
    if (FAILED(ec)) {
        return ec;
    }
    m_threadState = ThreadState_Running;

    return NOERROR;
}

ECode CThread::Initialize()
{
    ECode ec = NOERROR;
    Int32 i;

    assert(ThreadState_Unstarted == m_threadState);

    ec = (ECode)AuraEvent_Create(AURA_TRUE, AURA_UNSIGNALED, &m_hQuitEvent);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = (ECode)AuraCriticalSection_Create(&m_hObjLock);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = (ECode)AuraCriticalSection_Create(&m_hTlsLock);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = AuraThread_MakeThreadName((Aura_Address)m_userEntry,
                            (Aura_PWChar)m_name.GetPayload(),
                            MAXIMUM_THREAD_NAME_LENGTH);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    for (i = 0; i < TLS_TOTAL_SLOTS; i++) {
        m_tlsValue[i] = NULL;
    }

    AuraSystem_GetTime((Aura_SystemTime *)&m_startTime);

    return NOERROR;

ErrorExit:
    if (NULL != m_hTlsLock) {
        AuraCriticalSection_Destroy(m_hTlsLock);
        m_hTlsLock = NULL;
    }
    if (NULL != m_hObjLock) {
        AuraCriticalSection_Destroy(m_hObjLock);
        m_hObjLock = NULL;
    }
    if (NULL != m_hQuitEvent) {
        AuraEvent_Destroy(m_hQuitEvent);
        m_hQuitEvent = NULL;
    }
    return ec;
}

PInterface CThread::Probe(
    /* [in] */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(IThread *)this;
    }
    else if (riid == EIID_IThread) {
        return (IThread *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CThread;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface)&EIID_IThread;
    }
    else if (riid == EIID_IThreadContext) {
        return (IThreadContext *) this;
    }

    return NULL;
}

UInt32 CThread::AddRef()
{
    Int32 nRef = m_Refs.Increment();
    return (UInt32)nRef;
}

UInt32 CThread::Release()
{
    Int32 nRef;

    nRef = m_Refs.Decrement();
    if (nRef == 0) {
        delete this;
    }
    return (UInt32)nRef;
}

ECode CThread::Aggregate(
    /* [in] */ AggrType aggrType,
    /* [in] */ PInterface pObject)
{
    return E_NOT_IMPLEMENTED;
}

ECode CThread::GetDomain(
    /* [out] */ PInterface *ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CThread::Start()
{
    ECode ec = NOERROR;

    assert(AURA_INVALID_TID != m_threadId);

    Lock(m_hObjLock);
    if (ThreadState_Unstarted != m_threadState) {
        ec = NOERROR;
        goto ErrorExit;
    }
    ec = (ECode)AuraThread_Resume(m_hThread);
    if (FAILED(ec)) {
        // Do NOT change the state.
        goto ErrorExit;
    }
    AuraSystem_GetTime((Aura_SystemTime *)&m_startTime);
    m_threadState = ThreadState_Running;
    Unlock(m_hObjLock);

    //assert(NULL != m_pOwner);
    //((CProcess *)m_pOwner)->AttachThread(this);

    return ec;

ErrorExit:
    Unlock(m_hObjLock);
    return ec;
}

ECode CThread::Suspend()
{
    ECode ec = NOERROR;

    Lock(m_hObjLock);
    switch (m_threadState) {
        case ThreadState_Unstarted:
            ec = E_INVALID_OPERATION;
            break;
        case ThreadState_Finished:
            ec = E_THREAD_ALREADY_FINISHED;
            break;
        case ThreadState_Running:
        case ThreadState_Sleeping:
            //case ThreadState_InterruptibleSleeping:
            if (IsCurrentThread(this)) {
                m_threadState |= ThreadState_Suspended;
                Unlock(m_hObjLock);
                return (ECode)AuraThread_Suspend(m_hThread);
            }
            else {
                ec = (ECode)AuraThread_Suspend(m_hThread);
                if (FAILED(ec)) {
                    // Here, dont change the thread state.
                    break;
                }
                m_threadState |= ThreadState_Suspended;
            }
        case ThreadState_Suspended:
        case ThreadState_SleepingSuspended:
            m_nestedSuspend.Increment();
            break;
        default:
            break;
    }
    Unlock(m_hObjLock);

    return ec;
}

ECode CThread::Resume()
{
    ECode ec = NOERROR;

    Lock(m_hObjLock);
    if (ThreadState_Suspended != \
        (m_threadState & ThreadState_Suspended)) {
        ec = E_INVALID_OPERATION;
        goto ErrorExit;
    }

    if (m_nestedSuspend.Decrement() > 0) {
        goto SuccExit;
    }

    ec = (ECode)AuraThread_Resume(m_hThread);
    if (FAILED(ec)) {
        // Do NOT change the state.
        goto ErrorExit;
    }
    m_threadState &= ~ThreadState_Suspended;

ErrorExit:
SuccExit:
    Unlock(m_hObjLock);
    return ec;
}

ECode CThread::Join(
    /* [in]  */ Millisecond32 timeout,
    /* [out] */ WaitResult *pResult)
{
    IThread * pThread = NULL;
    ECode ec = NOERROR;
    WaitResult wr;

    if (::IsCurrentThread((IThread *)this)) {
        return E_INVALID_OPERATION;
    }

    void * pLock = _Elastos_SequencedContext_Leave();

    CPseudoKernel::Thread_GetCurrent(&pThread);
    ((CThread *)pThread)->SetThreadState(ThreadState_Sleeping);
    ec = (ECode)AuraEvent_Wait(m_hQuitEvent, (Aura_Millisecond32)timeout, (Aura_WaitResult *)&wr, AURA_NULL);
    ((CThread *)pThread)->SetThreadState(ThreadState_Running);
    pThread->Release();

    _Elastos_SequencedContext_Enter(pLock);

    if (NULL != pResult) {
        *pResult = wr;
    }
    return ec;
}

ECode CThread::Interrupt()
{
    Lock(m_hObjLock);
    if (ThreadState_Finished == m_threadState) {
        Unlock(m_hObjLock);
        return E_THREAD_ALREADY_FINISHED;
    }
    AuraThread_Interrupt(m_hThread);

    Unlock(m_hObjLock);

    return NOERROR;
}

ECode CThread::Abort()
{
    if (::IsMainThread((IThread *)this)) {
        m_pOwner->Kill(); // never returned.
        assert(0);
    }

    if (::IsCurrentThread((IThread *)this)) {
        ((CProcess *)m_pOwner)->DetachThread(this);
        this->DoAtQuit(E_THREAD_ABORTED, FALSE);
        this->Release();
        AuraThread_Quit(AURA_E_THREAD_ABORTED);
        assert(0);
    }

    Lock(m_hObjLock);
    if (ThreadState_Unstarted == m_threadState) {
        Unlock(m_hObjLock);
        //return E_THREAD_UNSTARTED;
        return  NOERROR;
    }
    else if (ThreadState_Finished == m_threadState) {
        Unlock(m_hObjLock);
        //return E_THREAD_ALREADY_FINISHED;
         return  NOERROR;
    }
    else {
        Unlock(m_hObjLock);
        ((CProcess *)m_pOwner)->DetachThread(this);
        this->DoAtQuit(E_THREAD_ABORTED, TRUE);
        this->Release();
        return NOERROR;
    }
}

ECode CThread::SetPriority(
    /* [in] */ ThreadPriority priority)
{
	if ((priority < ThreadPriority_Lowest)
	    || (priority > ThreadPriority_Highest)) {
	   	return E_INVALID_ARGUMENT;
	}
	if (ThreadState_Finished == m_threadState) {
        return E_THREAD_ALREADY_FINISHED;
    }

    return (ECode)AuraThread_SetPriority(m_hThread, (Aura_ThreadPriority)priority);
}

ECode CThread::GetPriority(
    /* [out] */ ThreadPriority * pPriority)
{
    if (NULL == pPriority) {
        return E_INVALID_ARGUMENT;
    }

    if (ThreadState_Finished == m_threadState) {
        return E_THREAD_ALREADY_FINISHED;
    }
    return (ECode)AuraThread_GetPriority(m_hThread, (Aura_ThreadPriority *)pPriority);
}

ECode CThread::GetId(
    /* [out] */ ThreadId * pTid)
{
    if (NULL == pTid) {
        return E_INVALID_ARGUMENT;
    }

    *pTid = m_threadId;
    return NOERROR;
}

ECode CThread::SetName(
    /* [in] */ WString name)
{
    Lock(m_hObjLock);
    if (name.IsNullOrEmpty()) {
        m_name.Copy(L"/0");
        Unlock(m_hObjLock);
        return NOERROR;
    }

    Int32 length = name.GetLength(MAXIMUM_THREAD_NAME_LENGTH);
    if (length < 0) {
        Unlock(m_hObjLock);
        return E_INVALID_ARGUMENT;
    }
    m_name.Copy(name);
    Unlock(m_hObjLock);

    return NOERROR;
}

ECode CThread::GetName(
    /* [out] */ WStringBuf * pName)
{
    if ((NULL == pName)
        || (pName->GetCapacity() < MAXIMUM_THREAD_NAME_LENGTH)) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hObjLock);
    pName->Copy(m_name);
    Unlock(m_hObjLock);

    return NOERROR;
}

ECode CThread::GetState(
    /* [out] */ ThreadState * pState)
{
    if (NULL == pState) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hObjLock);
    *pState = m_threadState;
    Unlock(m_hObjLock);

    return NOERROR;
}

ECode CThread::GetQuitCode(
    /* [out] */ QuitCode * pQuitCode)
{
    if (NULL == pQuitCode) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hObjLock);
    if (ThreadState_Finished != m_threadState) {
        Unlock(m_hObjLock);
        return E_THREAD_NOT_STOPPED;
    }
    *pQuitCode = m_quitCode;
    Unlock(m_hObjLock);

    return NOERROR;
}

ECode CThread::GetStartTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hObjLock);
    if (ThreadState_Unstarted == m_threadState) {
        Unlock(m_hObjLock);
        return E_THREAD_UNSTARTED;
    }

    *pTime = m_startTime;
    Unlock(m_hObjLock);

    return NOERROR;
}

ECode CThread::GetQuitTime(
    /* [out] */ SystemTime * pTime)
{
    if (NULL == pTime) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hObjLock);
    if (ThreadState_Finished != m_threadState) {
        Unlock(m_hObjLock);
        return E_THREAD_NOT_STOPPED;
    }
    *pTime = m_quitTime;
    Unlock(m_hObjLock);

    return NOERROR;
}

ECode CThread::GetProcessorTimes(
    /* [out] */ Millisecond32 * pUserTime,
    /* [out] */ Millisecond32 * pKernelTime)
{
    if ((NULL == pUserTime)
     && (NULL == pKernelTime)) {
        return E_INVALID_ARGUMENT;
    }

    return AuraThread_GetProcessorTimes(m_hThread,
                            (Aura_Millisecond32 *)pUserTime,
                            (Aura_Millisecond32 *)pKernelTime);
}

ECode CThread::Quit()
{
    //to be implemented
    return E_NOT_IMPLEMENTED;
}

void CThread::DoAtQuit(
    /* [in] */ QuitCode quitCode,
    /* [in] */ Boolean bCallAbortion)
{
    Lock(m_hObjLock);
    if (ThreadState_Finished == m_threadState) {
        Unlock(m_hObjLock);
        return ;
    }
    m_threadState = ThreadState_Finished;
    m_quitCode = quitCode;
    AuraSystem_GetTime((Aura_SystemTime *)&m_quitTime);
    AuraEvent_Signal(m_hQuitEvent, AURA_SIGNALED);
    Unlock(m_hObjLock);
    this->TransferInfo(quitCode);

    if (bCallAbortion) {
        AuraThread_Abort(m_hThread, (Aura_QuitCode)quitCode);
    }

    return ;
}

ECode CThread::GetLocalSlotValue(
    /* [in]  */ Int32 index,
    /* [out] */ PVoid * ppValue)
{
    assert((index >= 0) && (index < TLS_TOTAL_SLOTS));
    assert(NULL != ppValue);

    Lock(m_hTlsLock);
    *ppValue = m_tlsValue[index];
    Unlock(m_hTlsLock);

    return NOERROR;
}

ECode CThread::SetLocalSlotValue(
    /* [in]  */ Int32 index,
    /* [in]  */ PVoid pValue)
{
    assert ((index >= 0) && (index < TLS_TOTAL_SLOTS));

    Lock(m_hTlsLock);
    m_tlsValue[index] = pValue;
    Unlock(m_hTlsLock);

    return NOERROR;
}

void CThread::TransferInfo(QuitCode quitCode)
{
    CPseudoCentral::Thread_TransferInfo(m_threadId,
                                    quitCode,
                                    m_startTime,
                                    m_quitTime,
                                    m_name);
    return ;
}

void CThread::SetThreadState(
    /* [in]  */ ThreadState state)
{
    Lock(m_hObjLock);
    m_threadState = state;
    Unlock(m_hObjLock);

    return;
}

// create a thread object for main thread.
ECode CThread::S_Create(
    /* [in] */ IProcess * pOwner,
    /* [out] */ IThread ** ppThread)
{
    CThread * pThread = NULL;
    ECode ec = NOERROR;

    assert(NULL != pOwner);
    assert(NULL != ppThread);

    pThread = new CThread(pOwner);
    if (NULL == pThread) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    ec = pThread->InitializeAsMainThread();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    *ppThread = (IThread *)pThread;
    (*ppThread)->AddRef();

    return NOERROR;

ErrorExit:
    if (NULL != pThread) {
        delete pThread;
        pThread = NULL;
    }
    return ec;
}

// create a thread object for normal thread, including creating thread.
ECode CThread::S_Create(
    /* [in] */ IProcess * pOwner,
    /* [in] */ Address threadMainStartup,
    /* [in] */ Address threadRoutine,
    /* [in] */ Address threadArg,
    /* [in] */ ThreadCreationFlags flags,
    /* [out] */ IThread ** ppThread)
{
    CThread * pThread = NULL;
    ECode ec = NOERROR;

    assert(NULL != pOwner);
    assert(NULL != ppThread);

    pThread = new CThread(pOwner, threadRoutine, threadArg);
    if (NULL == pThread) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    ec = pThread->Initialize();
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    pThread->AddRef();
    ec = (ECode)AuraThread_Create((PAuraThreadMain)threadMainStartup,
                        (Aura_PVoid)(IThread *)pThread,
                        Aura_ThreadCreationFlag_Unstarted,
                        &pThread->m_hThread,
                        (Aura_ThreadId *)&pThread->m_threadId);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    *ppThread = (IThread *)pThread;
    (*ppThread)->AddRef();

    if (ThreadCreationFlag_Unstarted != flags) {
        pThread->Start();
    }

    return NOERROR;

ErrorExit:
    if (NULL != pThread) {
        delete pThread;
        pThread = NULL;
    }

    return ec;
}

ECode CThread::GetContext(ArrayOf<Int32> *pContextArray)
{
    Aura_ThreadContext * pContext = NULL;
    ECode ec = NOERROR;

    if ((NULL == pContextArray)
        || (pContextArray->GetLength() * sizeof(Int32) \
           < (Int32)sizeof(Aura_ThreadContext))) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hObjLock);
    if (m_threadState == ThreadState_Finished) {
        ec = E_THREAD_ALREADY_FINISHED;
        goto ErrorExit;
    }

    pContext = (Aura_ThreadContext *)pContextArray->GetPayload();
    ec = AuraThread_GetContext(m_hThread, pContext);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    Unlock(m_hObjLock);

    return NOERROR;

ErrorExit:
    Unlock(m_hObjLock);
    return ec;
}

ECode CThread::SetContext(const ArrayOf<Int32> &contextArray)
{
    Aura_ThreadContext * pContext = NULL;
    ECode ec = NOERROR;

    Lock(m_hObjLock);
    if (m_threadState == ThreadState_Finished) {
        ec = E_THREAD_ALREADY_FINISHED;
        goto ErrorExit;
    }
    pContext = (Aura_ThreadContext *)contextArray.GetPayload();
    ec = AuraThread_SetContext(m_hThread, pContext);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    Unlock(m_hObjLock);

    return NOERROR;

ErrorExit:
    Unlock(m_hObjLock);
    return ec;
}

ECode CThread::GetUserStackTop(Address *pStackTop)
{
    ECode ec = NOERROR;

    if (NULL == pStackTop) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hObjLock);
    if (m_threadState == ThreadState_Finished) {
        ec = E_THREAD_ALREADY_FINISHED;
        goto ErrorExit;
    }
    ec = AuraThread_GetUserDesktop(m_hThread, (Aura_Address *)pStackTop);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    Unlock(m_hObjLock);

    return NOERROR;

ErrorExit:
    Unlock(m_hObjLock);
    return ec;
}

